-
Notifications
You must be signed in to change notification settings - Fork 24.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SQL: move requests' parameters to requests JSON body #36149
Conversation
Pinging @elastic/es-search |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, left some comments mostly about repetition of code.
...sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java
Outdated
Show resolved
Hide resolved
import org.elasticsearch.common.ParseField; | ||
|
||
final class SqlField { | ||
public static final ParseField MODE = new ParseField("mode"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, overlooked this class. Will remove it and use the interface inside AbstractSqlQueryRequest
. Thanks for catching.
|
||
import org.elasticsearch.common.ParseField; | ||
|
||
final class SqlField { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can be an interface, and also the name could be SqlRequestField
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will use the interface inside AbstractSqlQueryRequest
. Renamed to SqlRequestField
.
@@ -47,8 +51,22 @@ public RestSqlTranslateAction(Settings settings, RestController controller) { | |||
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { | |||
SqlTranslateRequest sqlRequest; | |||
try (XContentParser parser = request.contentOrSourceParamParser()) { | |||
sqlRequest = SqlTranslateRequest.fromXContent(parser, Mode.fromString(request.param("mode"))); | |||
sqlRequest = SqlTranslateRequest.fromXContent(parser); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block is repeated here: https://github.com/elastic/elasticsearch/pull/36149/files#diff-47894e3a968e76739b45d73c3405ab8eR61 and here: https://github.com/elastic/elasticsearch/pull/36149/files#diff-33543baa55df79bc3941f885b6fcfa61R47 so maybe extract it to a method.
@@ -140,6 +140,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws | |||
if (query != null) { | |||
builder.field("query", query); | |||
} | |||
if (requestInfo() != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
code is repeated here: https://github.com/elastic/elasticsearch/pull/36149/files#diff-7e3b87bb842194e7f25b2a712cb821e6R46 please extract to a method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has this been resolved because it still shows up?
@@ -157,7 +158,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws | |||
builder.endArray(); | |||
|
|||
if (cursor.equals("") == false) { | |||
builder.field(SqlQueryRequest.CURSOR.getPreferredName(), cursor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need the CURSOR
and FILTER
in org.elasticsearch.xpack.sql.action.SqlQueryRequest
?
assertThat(e.getMessage(), containsString("unknown field [key]")); | ||
} | ||
|
||
public void testClearCursorRequestParser() throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also check for missing completely client.id
and mode
.
If you extract the parsing in a method then you could also unit test the method so we can avoid checking for all different types of requests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments mainly around encapsulation.
ParseField PAGE_TIMEOUT = new ParseField("page_timeout"); | ||
ParseField FILTER = new ParseField("filter"); | ||
ParseField MODE = new ParseField("mode"); | ||
ParseField CLIENT_ID = new ParseField("client.id"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be client_id
not client.id
- helps with JSON and it is consistent with the other params.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I planned to change the parameter name after this PR, but I can do it now, as well.
parser.declareString(AbstractSqlQueryRequest::query, SqlRequestField.QUERY); | ||
parser.declareString((request, mode) -> { | ||
Mode m = Mode.fromString(mode); | ||
if (request.requestInfo() != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be better handled inside the request object so there are no null checks.
Either there's a default requestInfo so one can just call request.info().clientId() or do request.clientId(..) and internally set that up.
} | ||
}, SqlRequestField.MODE); | ||
parser.declareString((request, clientId) -> { | ||
if (request.requestInfo() != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above.
(String) objects[0] | ||
)); | ||
private static final ConstructingObjectParser<SqlClearCursorRequest, RequestInfo> PARSER = | ||
// here the position in "objects" is the same as the fields parser declarations below |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the comment misaligned with the object below?
@@ -31,15 +32,17 @@ | |||
|
|||
// TODO: Simplify cursor handling | |||
private String cursor; | |||
private Mode mode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need the mode in the response? Do clients consume it in any way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use it to output dates in different formats, depending on mode (jdbc/odbc VS. plain). See
Line 174 in b32f545
if (Mode.isDriver(mode)) { |
public Mode testMode; | ||
|
||
public RequestInfo requestInfo; | ||
public String clientId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there a clientId
and RequestInfo
; the former is contained in the latter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed this one. Will fix.
if (mode == null) { | ||
try { | ||
return Mode.valueOf(mode.toUpperCase(Locale.ROOT)); | ||
} catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should just thrown an exception stating the mode is invalid. I think it's good to be lenient but at the same time simply don't set up a mode instead of setting an incorrect one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done this way because mode
is a required parameter, even if a client can choose not to send one or send the wrong one. It's just required everywhere in the code and I kept the same behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's a required param let's use a default value for it so in case the client doesn't send one, we default to PLAIN which is what we do anyway.
However if the client sends an incorrect value we should report this not silently ignore it.
AbstractSqlRequest sqlRequest = initializeSqlRequest(request); | ||
|
||
// no mode specified, default to "PLAIN" | ||
if (sqlRequest.requestInfo() == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constant checking of requestInfo is trappy - simply set a default one for PLAIN and no client id so there's no risk of getting a NPE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some leftovers.
if (mode == null) { | ||
try { | ||
return Mode.valueOf(mode.toUpperCase(Locale.ROOT)); | ||
} catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's a required param let's use a default value for it so in case the client doesn't send one, we default to PLAIN which is what we do anyway.
However if the client sends an incorrect value we should report this not silently ignore it.
@@ -37,6 +38,12 @@ public String clientId() { | |||
} | |||
|
|||
public void clientId(String clientId) { | |||
if (clientId != null) { | |||
clientId = clientId.toLowerCase(Locale.ROOT); | |||
if (!clientId.equals(CLI) && !clientId.equals(CANVAS)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make this pluggable by checking clientId against a list of known values/whitelist.
I mention this since for odbc we're looking into potentially sending extra information (32 vs 64 bit, maybe some OS info) and this would make it easier - just add the item to the whitelist.
@@ -43,6 +43,14 @@ public int hashCode() { | |||
@Override | |||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | |||
builder.field("cursor", cursor); | |||
if (requestInfo() != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes reading inconsistent since a field might appear or not depending on its value. How about using nullValue
?
Also mode is always required so the check should not be performed for it no?
@@ -140,6 +140,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws | |||
if (query != null) { | |||
builder.field("query", query); | |||
} | |||
if (requestInfo() != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has this been resolved because it still shows up?
@@ -20,8 +21,8 @@ public RequestInfo(Mode mode) { | |||
} | |||
|
|||
public RequestInfo(Mode mode, String clientId) { | |||
this.mode = mode; | |||
this.clientId = clientId; | |||
mode(mode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we expect the requestInfo to be modified after the data is passed through the constructor?
If not, the setters are not needed and the fields can be made final.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ObjectParser
here needs access to individual methods to set mode
and client_id
respectively.
@@ -235,4 +238,17 @@ public boolean equals(Object o) { | |||
public int hashCode() { | |||
return Objects.hash(super.hashCode(), query, timeZone, fetchSize, requestTimeout, pageTimeout, filter); | |||
} | |||
|
|||
public interface SqlRequestField { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move these fields inside the class itself, make them private/default and remove the interface.
@@ -718,6 +711,10 @@ private static int getOpenContexts(Map<String, Object> stats, String index) { | |||
public static String randomMode() { | |||
return randomFrom("", "jdbc", "plain"); | |||
} | |||
|
|||
public static String mode(String mode) { | |||
return mode.isEmpty() ? "" : ",\"mode\":\"" + mode + "\""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strings.isEmpty is a better choice as it checks if it's also null.
run the gradle build tests 2 |
run the gradle build tests 2 |
run the gradle build tests 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -10,6 +10,7 @@ | |||
import org.apache.http.HttpEntity; | |||
import org.apache.http.entity.ContentType; | |||
import org.apache.http.entity.StringEntity; | |||
import org.apache.logging.log4j.util.Strings; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like an incorrect import.
response = runSql(mode, new StringEntity("{\"cursor\":\"" + cursor + "\"}", | ||
ContentType.APPLICATION_JSON)); | ||
response = runSql(new StringEntity("{\"cursor\":\"" + cursor + "\"" + mode(mode) + "}", | ||
ContentType.APPLICATION_JSON), ""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"" could be StringUtils/Strings.EMPTY
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added an EMPTY constant to org.elasticsearch.xpack.sql.proto.StringUtils
. It seems there is no other class visible to qa
that has this constant defined.
} | ||
|
||
private String cursor; | ||
|
||
public SqlClearCursorRequest() { | ||
|
||
super(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no value in adding super
as this constructor is called implicitly anyway.
@@ -17,7 +17,7 @@ | |||
ODBC; | |||
|
|||
public static Mode fromString(String mode) { | |||
if (mode == null) { | |||
if (mode == null || mode.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potentially Strings.isEmpty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no utility class visible to this class that has an isEmpty()
method useful here. Or I missed it... which is very likely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
run the gradle build tests 1 |
(cherry picked from commit eead8a1)
* elastic/master: (36 commits) Add check for minimum required Docker version (elastic#36497) Minor search controller changes (elastic#36479) Add default methods to DocValueFormat (elastic#36480) Fix the mixed cluster REST test explain/11_basic_with_types. Modify `BigArrays` to take name of circuit breaker (elastic#36461) Move LoggedExec to minimumRuntime source set (elastic#36453) added 6.5.4 version Add test logging for elastic#35644 Tests- added helper methods to ESRestTestCase for checking warnings (elastic#36443) SQL: move requests' parameters to requests JSON body (elastic#36149) [Zen2] Respect the no_master_block setting (elastic#36478) Require soft-deletes when access changes snapshot (elastic#36446) Switch more tests to zen2 (elastic#36367) [Painless] Add extensive tests for def to primitive casts (elastic#36455) Add setting to bypass Rollover action (elastic#36235) Try running CI against Zulu (elastic#36391) [DOCS] Reworked the shard allocation filtering info. (elastic#36456) Log [initial_master_nodes] on formation failure (elastic#36466) converting ForbiddenPatternsTask to .java (elastic#36194) fixed typo ...
Fixes #35992.